前言
很早之前就使用redis 了,但都没有好好总结过,来一次吧
一、基础概述
- Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。
1、Redis支持的数据结构
- strings(字符串)
- hashes(散列)
- lists(列表)
- sets(集合)
- zset (sorted sets有序集合)
- bitmaps (位图)
- hyperloglogs (日志)
- geospatial(地理空间)
- streams (流)
二、redis 持久化
Redis的持久化有2种方式
- 快照(RDB)
- 日志(AOF)
1、Rdb快照的配置选项
- RDB的配置如下:
save 900 1 # 900内,有1条写入,则产生快照 |
- 每隔N秒或N次写操作后, 从内存dump数据形成rdb文件
- 可以手动保存dump文件,使用命令:
save
或者bgsave
命令 - 两者的区别:
- save只管保存,其他不管,全部阻塞,bgsave 会在后台异步进行快照操作,还可以响应客户端请求
2、AOF 的配置
- 开启AOF,配置如下:
appendonly no # 是否打开 aof日志功能 |
- 把所有可写的操作都记录下来
- 可以手动重写使用命令:
bgrewriteaof
命令 - 如果aof 文件出现异常,可以使用bin目录下的
redis-check-aof
来修复,命令:redis-check-aof -fix test.aof
. - 同理,如果 dump 文件出现异常也可以使用
redis-check-dump
来修复。
3、常见问题
问: 在dump rdb过程中,aof如果停止同步,会不会丢失? |
三、过期策略
- redis我们可以给key设置一个过期时间,到时间会删除。redis是如果做到过期删除的呢?
- 我们知道,redis设置过期的方法有
expires()
方法。它的实现原理是怎样的? - redis针对TTL时间有专门的dict进行存储,就是redisDb当中的
dict *expires
字段。 - dict顾名思义就是一个
hashtable
,key为对应的rediskey,value为对应的TTL时间。 - TTL设置key的过期方法有如下:
expire() 按照相对时间且以秒为单位的过期策略 |
- 判断key是否过期,key去db->expires找到过期时间对象,然后与当前系统时间相比计算差值
- 如果键没有设置过期时间,那么返回 -1,否则返回剩余时间
- 那key过期了,redis怎么知道,并且把key删了呢?
- Redis 删除失效主键的方法主要有两种:
passive
(惰性方法) 和active
(主动方法) passive
:在键被访问时如果发现它已经失效,那么就删除它active
:定期去扫描键空间,删除过期的key- 详细介绍如下:
1、过期删除策略
惰性方法
- 在大致了解了Redis是如何维护设置了失效时间的主键之后
- 我们就先来看一看 Redis 是如何实现消极地删除失效主键的
- Redis 有一个函数:
expireIfNeeded()
,Redis 在实现GET
、MGET
、HGET
、LRANGE
等所有涉及到读取数据的命令时都会调用它。 - 这个函数的内容如下:
int expireIfNeeded(redisDb *db, robj *key) { |
- 通过一些方法名大致可以知道这个方法主要是判断key是否失效的。
- 有个主要的地方就是:
propagateExpire()
方法,这个是广播失效的key给到当前Redis服务器的所有 Slave - 告知这些 Slave 删除各自的失效的key
主动方法:
- 通过对
expireIfNeeded()
函数的介绍了解了 Redis 是如何以一种消极的方式删除失效主键的 - 如果某些失效的主键迟迟等不到再次访问的话,Redis 就永远不会知道这些主键已经失效.
- 也就永远也不会删除它们了,这无疑会导致内存空间的浪费。Redis还准备了一招积极的删除方法
- 通过Redis的时间事件来实现,即每隔一段时间就中断一下完成一些指定操作,其中就包括检查并删除失效主键。
- 这里我们说的时间事件就是
serverCron
,它在 Redis 服务器启动时创建,在源码:server.c
中的initServer()
方法中 - 有个
aeCreateTimeEvent()
的方法,创建时间事件回调来处理一些后台任务,如: 逐步执行操作,例如客户端超时,过期key,BGSAVE和AOF的触发等等 - 我们主要看过期key,而处理过期key的方法是:
expire.c
里面的activeExpireCycle()
方法。 - activeExpireCycle执行删除key的方法是:
activeExpireCycleTryExpire()
。 源码(版本:6.2.3)如下:
/* Helper function for the activeExpireCycle() function. |
- 此外
activeExpireCycle
函数还有处理时间上的限制,不是想执行多久就执行多久,凡此种种都只有一个目的。 - 那就是避免失效主键删除占用过多的CPU资源。
- 参考原文链接:https://www.aidandai.com/posts/redis-expire.html
其实我们从redis.conf的配置文件中可以看到介绍:
- 部分介绍说明:
# Redis reclaims expired keys in two ways: upon access when those keys are |
- 但是这里还是有问题
- 因为不管主动还是被动删除key,都可能会有部分key没有及时清理(因为主动扫描是有时间限制的,而被动删除可能你没有访问它就不会删除)
- 如果大量过期 key 堆积在内存里,导致 redis 内存块耗尽了,咋整?
- 答案:内存淘汰机制
2、Redis的内存淘汰策略
- 当Redis达到最大内存时,Redis将如何选择要删除的内容。
- 可以选择的策略有如下
maxmemory-policy:
- noeviction:
当内存不足以容纳新写入数据时,新写入操作会报错。默认策略 - volatile-lru:
当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。 - allkeys-lru:
当内存不足以容纳新写入数据时,在所有键空间中,移除最近最少使用的key。 - volatile-lfu:
在设置了过期时间的键值对中,移除最近最不频繁使用的键值对 - allkeys-lfu:
在所有的键值对中,移除最近最不频繁使用的键值对 - volatile-random:
当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。 - allkeys-random:
当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。 - volatile-ttl:
当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
LRU
表示最近最少使用,LFU
表示最少经常使用
四、消息订阅与发布
1、消息的订阅
|
2、发布消息
|
五、Redis 事务
- redis也是支持事务的哦
1、redis 与mysql事务的对比
mysql | redis | |
---|---|---|
开启 | start transaction | multi |
语句 | 普通的sql语句 | 普通的命令 |
失败 | rollback 回滚 | discard 取消 |
成功 | commit | exec |
注意一: rollback与discard 的区别
如果已经成功执行了2条语句, 第3条语句出错.
Rollback后,前2条的语句影响消失.
Discard只是结束本次事务,前2条语句造成的影响仍然还在
注意二:在multi后面的语句中, 语句出错可能有2种情况
1: 语法就有问题,
这种,exec时,报错, 所有语句得不到执行2: 语法本身没错,但适用对象有问题. 比如 zadd 操作list对象
Exec之后,会执行正确的语句,并跳过有不适当的语句.
(如果zadd操作list这种事怎么避免? 这一点,由程序员负责)
2、watch命令
Redis的事务中,启用的是乐观锁,只负责监测key没有被改动。所以我们有需要的可以watch
监听某个值是否发生了变化。
例:
redis 127.0.0.1:6379> watch ticket |
watch key1 key2 ... keyN
作用:监听key1 key2..keyN有没有变化,如果有变, 则事务取消
unwatch
作用: 取消所有watch监听
慢慢更新吧